﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DataSource;
using System.Collections.Concurrent;
using CsvHelperPlus;
using System.IO;
using System.Data;


namespace Strategy.Linkage.Version1
{
    public class Linkage
    {
        //ConcurrentDictionary<string, Level2Struct> lv2ht;
        List<DateTime> observeTimeList;
        List<string> tickerList;
        private string date;
        private int timePos; // 观测时间的播放位置

        TimeSpan leaderEntryTimeSpan; //龙头进入信号时，考虑的时间范围
        TimeSpan followerEntryTimeSpan; //跟随者的机会时间。超出无效
        TimeSpan decayTimeSpan; // 退出信号时，考虑的时间范围
        TimeSpan falseTimeSpan;
        TimeSpan timeout;
        TimeSpan oneMinuteSpan;
        TimeSpan halfMinuteSpan;
        TimeSpan snapTimeSpan; //需要保存历史数据的实际范围

        public delegate void SignalEventHandler(Follow fl);
        public event SignalEventHandler OnEntrySignalEvent;
        public event SignalEventHandler OnExitSignalEvent;

        T0Univ tu; // 保存t0univ的相关信息
        Dictionary<string, Leader> leaderDict;
        HashSet<string> followSet; //保存已跟随龙头的股票
        SectorHelp sectHandler;
        string sectorType;

        public Linkage(string date)
        {
            //lv2ht = new ConcurrentDictionary<string, Level2Struct>();
            observeTimeList = new List<DateTime>();
            leaderEntryTimeSpan = new TimeSpan(0, 0, 0, 60);
            followerEntryTimeSpan = new TimeSpan(0,0, 0, 120);
            decayTimeSpan = new TimeSpan(0, 0, 0, 120);
            falseTimeSpan = new TimeSpan(0, 0, 0, 180);
            timeout = new TimeSpan(0, 0, 0, 120);
            oneMinuteSpan = new TimeSpan(0, 0, 0, 60);
            halfMinuteSpan = new TimeSpan(0, 0, 0, 30);
            snapTimeSpan = new TimeSpan(0, 0, 0, 180); //保存过去5分钟的历史数据
            tu = new T0Univ(snapTimeSpan);
            leaderDict = new Dictionary<string, Leader>();
            followSet = new HashSet<string>();
            timePos = 0;
            sectorType = SectorHelp.GetSectorType();
            sectHandler = new SectorHelp(DateUtils.GetLastTradeDate(date));
            this.date = date;
        }

        public void Subscrible()
        {
            SimulateMarket sm = SimulateMarket.GetInstance();
            //tickerList = new List<string>();
            //tickerList.Add("603711");
            //tickerList.Add("000002");
            //tickerList.Add("300465");
            //tickerList = sm.GetAllMarketStockList().Take(1000).ToList();
            //tickerList = sm.GetAllMarketStockList();
            tickerList = GetStockListByRank();
            sm.Subscrible(tickerList, DType.LV2);
        }

        private List<string> GetStockListByRank()
        {
            string lastTradeDate = DateUtils.GetLastTradeDate(date);
            SimulateMarket sm = SimulateMarket.GetInstance();
            Dictionary<string, Type> convert = new Dictionary<string,Type>();
            convert.Add("TA_AVG5", typeof(double));
            convert.Add("FLOATINGMARKETCAP", typeof(double));
            convert.Add("SIGMA_20", typeof(double));
            DataTable dt = sm.GetUniv(lastTradeDate, convert:convert);
            dt.Columns.Add("TurnOver", typeof(float));
            //dt.Columns.Add("Sigma", typeof(float));
            //dt.Columns.Add("Amt", typeof(float));
            for (int i = 0; i < dt.Rows.Count; i++)
            {
                if ((double)dt.Rows[i]["TA_AVG5"] < 0 || (double)dt.Rows[i]["FLOATINGMARKETCAP"] < 0) 
                {
                    dt.Rows[i]["TurnOver"] = -9999;
                }
                else
                {
                    dt.Rows[i]["TurnOver"] = (double)dt.Rows[i]["TA_AVG5"] / (double)dt.Rows[i]["FLOATINGMARKETCAP"];
                }
                //dt.Rows[i]["TurnOver"] = float.Parse(dt.Rows[i]["TA_AVG5"].ToString())/float.Parse(dt.Rows[i]["FLOATINGMARKETCAP"].ToString());
                //dt.Rows[i]["Sigma"] = float.Parse(dt.Rows[i]["SIGMA_20"].ToString());
                //dt.Rows[i]["Amt"] = float.Parse(dt.Rows[i]["TA_AVG5"].ToString()); 

            }
            List<string> stkListbyAmt = dt.AsEnumerable().OrderByDescending(x => x["TA_AVG5"]).Take((int)(dt.Rows.Count * 0.3)).Select(x => x["#TICKER"].ToString().Substring(0, 6)).ToList();
            List<string> stkListbySigma = dt.AsEnumerable().OrderByDescending(x => x["SIGMA_20"]).Take((int)(dt.Rows.Count * 0.6)).Select(x => x["#TICKER"].ToString().Substring(0, 6)).ToList();
            List<string> stkListbyTurnOver = dt.AsEnumerable().OrderByDescending(x => x["TurnOver"]).Take((int)(dt.Rows.Count * 0.3)).Select(x => x["#TICKER"].ToString().Substring(0, 6)).ToList();
            List<string> stkList = stkListbyAmt.Intersect(stkListbySigma).ToList().Intersect(stkListbyTurnOver).ToList();
            return stkList;
        }

        public void SetObserveTime(DateTime st, DateTime et, int second) //设定观测起始时间，结束时间，观测频率
        {
            TimeSpan interval = new TimeSpan(0, 0, 3);
            while (st <= et)
            {
                observeTimeList.Add(st);
                st = st + interval;
            }
        }


        public List<DateTime> GetTimeSlice()
        {
            return observeTimeList;
        }


        public void OnLevel2Received(Level2Struct ls) //收到行情进行保存
        {
            //lv2ht[ls.code] = ls;
            tu.Add(ls);
        }

        public void OnTimePulse(DateTime dt)  // 时间响应
        {
            tu.TimeSync(dt); // T0Uiv的时间同步
            if (IsOutOfTimeObserve())  //策略观察时间已结束就不响应时间
            {
                return;
            }
            while (GetCurrentTime() <= dt)
            {
                OnObserveTime();
                MoveToNextTime();
                if (IsOutOfTimeObserve()) break; 
            }
            
        }


        public bool IsOutOfTimeObserve()
        {
            return timePos > observeTimeList.Count - 1;
        }

        public bool IsMarkertDataAvailable(string code)
        {
            return SimulateMarket.GetInstance().IsMarketDataAvailable(code);
        }

        public void OnObserveTime()  // 实盘时替换成一个定时器调用的函数就行。
        {
            RunStrategy();
        }

        public DateTime GetCurrentTime()
        {
            return observeTimeList[timePos];
        }

        public void MoveToNextTime()
        {
            timePos++;
        }

        public void RunStrategy() //注意下面的顺序
        {
            FindTheLeader();
            SendExitSignal(); 
            SendEntrySignal();
        }

        public void FindTheLeader()
        {
            float ret;
            int vol;
            foreach(var tk in tickerList)
            {
                if (leaderDict.ContainsKey(tk)) //已经是龙头了
                {
                    continue;
                }
                ret = tu.CountCurRet(tk, GetCurrentTime() - leaderEntryTimeSpan);
                vol = 0;// tu.CountVol;
                if (ret >= 200) //找到龙头
                {
                    if (tk == "603711")
                    {
                        ;
                    }
                    List<string> sectList = sectHandler.GetSector(tk); //找到龙头所属板块,可能龙头属于多个板块
                    //List<string> candidate = new List<string>();
                    //foreach (var s in sect)
                    //{
                    //    candidate.AddRange(sectHandler.GetStockInSect(s)); //找到潜在的跟随者
                    //}
                    //candidate = candidate.Distinct().ToList();
                    //candidate.Remove(tk); //跟随者中要把龙头自己去掉
                    Hashtable candidate = new Hashtable();
                    foreach (var sect in sectList)
                    {
                        foreach (var code in sectHandler.GetStockInSect(sect))
                        {
                            if (candidate.ContainsKey(code))
                            {
                                continue;
                            }
                            else
                            {
                                candidate[code] = sect;
                            }
                        }
                    }
                    candidate.Remove(tk); //跟随者中要把龙头自己去掉

                    Leader ld = new Leader();
                    ld.code = tk;
                    ld.ret = ret;
                    ld.vol = vol;
                    ld.time = GetCurrentTime();
                    ld.sect = sectList;
                    ld.candidates = candidate;
                    leaderDict[tk] = ld; //加载到leadDict中
                }
            }
        }


        public void SendEntrySignal()
        {
            float ret;
            int vol = 0;
            foreach(var leader in leaderDict.Keys)
            {
                Leader ld = leaderDict[leader];

                if (GetCurrentTime() - ld.time > followerEntryTimeSpan) //龙头开始上涨已超过一定时间,就不纳入跟随者了
                {
                    continue;
                }
                foreach(string tk in ld.candidates.Keys)
                {
                    if (leaderDict.ContainsKey(tk)) continue; // 已是龙头，不当小弟
                    //if (followSet.Contains(tk)) continue; //已跟随龙头，不跟随其他龙头 
                    //ret = tu.CountMaxRet(tk, GetCurrentTime() - ld.time); // 龙头开始到2分钟结束之前，任意时刻满足要求就好
                    if ((tk == "002181") && (ld.code == "000513"))
                    {
                        ;
                    }
                    //if (GetCurrentTime() - ld.time < halfMinuteSpan) //小于30s的去掉
                    //{
                    //    continue;
                    //}

                    if (!IsMarkertDataAvailable(tk)) //交易所没有这票行情就不发信号
                    {
                        continue;
                    }

                    //ret = tu.CountCurRet(tk, GetCurrentTime() - thirtySecondSpan);
                    ret = 1000; //龙头出现，候选者无脑跟进
                    
                    if (ret > 100) // 找到fellow
                    {
                        Follow fl = new Follow();
                        fl.code = tk;
                        fl.entryRet = ld.ret; // 进入时，需要根据哪个ret判断，就记录哪个ret
                        fl.vol = vol;
                        fl.leader = ld.code;
                        fl.time = GetCurrentTime();
                        fl.sigStatus = SignalState.enterSignalSended; //准备发送进入信号
                        fl.leaderTime = ld.time;
                        //fl.sect = sectHandler.GetBothInSect(ld.code, fl.code)[0]; //公共板块
                        fl.sect = ld.candidates[tk].ToString();
                        ld.followers.Add(fl);
                        followSet.Add(tk); //列入有主（龙头）列表
                        OnEntrySignalEvent(fl);
                    }
                }
            }

        }

        public void SendExitSignal()
        {
            float ret;
            int vol = 0;
            foreach (var key in leaderDict.Keys.ToList())
            {
                Leader ld = leaderDict[key];
                for (int i = 0; i < ld.followers.Count; i++)
                {
                    Follow fl = ld.followers[i];
                    if (fl.sigStatus == SignalState.exitSignalSended) //已经退出，就不用进行下面的判断
                    {
                        continue;
                    }
                    TimeSpan span = GetCurrentTime() - fl.time; //进入信号发出后，至今的时间间隔
                    if (span > timeout) //超时
                    {
                        fl.sigStatus = SignalState.exitSignalSended;
                        fl.exitReason = ExitReason.timeOut;
                        followSet.Remove(fl.code); //取得自由身后，可以跟随新的龙头
                        OnExitSignalEvent(fl);
                        continue;
                    }
                    if(span >= halfMinuteSpan) //假信号
                    {
                        ret = tu.CountCurRet(fl.code, GetCurrentTime() - halfMinuteSpan); //最近x分钟涨幅
                        if (ret < 0)
                        {
                            fl.sigStatus = SignalState.exitSignalSended;
                            fl.exitReason = ExitReason.falsePositive;
                            fl.exitRet = ret;
                            followSet.Remove(fl.code);
                            OnExitSignalEvent(fl);
                            continue;
                        }
                    }
                    if((span >= halfMinuteSpan)) //信号衰减
                    {
                        float retLeader = tu.CountCurRet(ld.code, GetCurrentTime() - halfMinuteSpan);
                        //float retFollower = tu.CountCurRet(fl.code, GetCurrentTime() - oneMinuteSpan);
                        if ((retLeader < 50))
                        {
                            fl.sigStatus = SignalState.exitSignalSended;
                            fl.exitReason = ExitReason.signalDecay;
                            fl.exitRet = retLeader;
                            followSet.Remove(fl.code);
                            OnExitSignalEvent(fl);
                            continue;
                        }
                    }

                    if (span >= halfMinuteSpan) //止盈
                    {
                        ret = tu.CountCurRet(fl.code, GetCurrentTime() - halfMinuteSpan);
                        if (ret > 100)
                        {
                            fl.sigStatus = SignalState.exitSignalSended;
                            fl.exitReason = ExitReason.takeProfit;
                            fl.exitRet = ret;
                            followSet.Remove(fl.code);
                            OnExitSignalEvent(fl);
                            continue;
                        }
                    }

                }
                if(GetCurrentTime() - ld.time > new TimeSpan(0, 5, 0)) //5分钟后如果龙头还没有跟随者，就kill掉该龙头
                {
                    if(ld.followers.Count == 0)
                    {
                        leaderDict.Remove(ld.code);
                    }
                }

            }

        }



        public void Save(string path)
        {
            string dir = String.Format(@"{0}\Leaders", path); 
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }

            foreach(var file in Directory.GetFiles(dir))//删除该目录下原有文件
            {
                File.Delete(file);
            }

            foreach(var key in leaderDict.Keys)
            {
                Leader ld = leaderDict[key];
                string file = dir + "\\" + ld.code + ".csv";
                if (ld.followers.Count > 0)
                {
                    CsvHandler.ListSave2CSV(file, ld.followers);
                }
            }
        }


    }

    public class Leader
    {
        public string code;
        public List<string> sect;
        public List<Follow> followers;
        //public List<string> candidates;
        public Hashtable candidates; //成为follow的候选人，纳入观察的对象, value为对应板块
        public float ret; // 成为leader时的涨幅
        public float vol; // 成为leader时的1分钟成交量
        public DateTime time; // 成为leader的时间
        public DateTime dealTime; // 退出龙头的时间(如果其follow都已出仓,该龙头算作结束）
        public bool isDead; // 龙头是否结束
        public Leader()
        {
            isDead = false;
            sect = new List<string>();
            followers = new List<Follow>();
            //candidates = new List<string>(); // Leader所属板块的所有股票
            candidates = new Hashtable();
        }
    }

    public class Follow
    {
        public string code;
        public string leader;
        public DateTime leaderTime;
        public string sect; //与龙头同属的板块
        public float entryRet;
        public float exitRet;
        public float vol;
        public DateTime time; //成为跟屁虫的时间
        public SignalState sigStatus; //该follow是否正要进入
        public ExitReason exitReason;
        public Follow()
        {
            entryRet = 0;
            exitRet = 0;
            sigStatus = SignalState.noSignalSended;
            exitReason = ExitReason.notExit; //默认值
        }
    }

    public enum SignalState
    {
        noSignalSended = 0, //未发进仓信号
        enterSignalSended = 1, //已发进仓信号
        exitSignalSended = 2 // 已发出仓信号
    }


    public enum ExitReason
    {
        notExit = 0, //针对买单
        timeOut = 1,
        signalDecay = 2,
        falsePositive = 3,//误报
        takeProfit =4 //止盈
    }

}